package com.aliengen.thingple.driverapp.network.api;

import android.content.Context;
import android.util.Log;

import com.aliengen.thingple.driverapp.data.local.CustomerModel;
import com.aliengen.thingple.driverapp.data.local.EventModel;
import com.aliengen.thingple.driverapp.data.local.PlanGroupModel;
import com.aliengen.thingple.driverapp.data.local.PlanItemModel;
import com.aliengen.thingple.driverapp.data.local.ProductModel;
import com.aliengen.thingple.driverapp.data.local.TruckModel;
import com.aliengen.thingple.driverapp.data.models.Anomaly;
import com.aliengen.thingple.driverapp.data.models.CylinderTag;
import com.aliengen.thingple.driverapp.data.models.TruckTag;
import com.aliengen.thingple.driverapp.network.api.exception.APICallException;
import com.aliengen.thingple.driverapp.network.api.exception.FieldNotFoundException;
import com.aliengen.thingple.driverapp.network.volley.Network;
import com.aliengen.thingple.driverapp.network.volley.ResponseErrorHandler;
import com.android.volley.Cache;
import com.android.volley.Response;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;

/**
 * This class is part of the Thingple mobile application for the CMC168 Handheld reader.
 *
 * Copyright AlienGen Limited 2016. All rights reserved.
 *
 * @author Timothé Mermet-Buffet <tim@aliengen.com>
 */
public class ThingpleAPI extends HMACAuthenticator {

    public ThingpleAPI(Network network, Context context)
    {
        super(network, context);
    }

    public interface Listener<T> {
        public void onResponse(T object);
        public void onError(String message);
    }

    public void getTruck(final TruckTag truck, final ThingpleAPI.Listener<TruckModel> listener) throws APICallException {
        this.get("/" + truck.getTruckId() + "/truck/" + this.getDeviceId(), new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                /**
                 * Unserialize truck informations.
                 */
                try {
                    if(!response.has("data")) {
                        throw new FieldNotFoundException("Bad response from server, field ");
                    }

                    try {
                        TruckModel t = new TruckModel(response.getJSONObject("data"));
                        truck.setTruck(t);
                        listener.onResponse(t);
                    } catch (JSONException e)
                    {
                        listener.onError(e.getMessage());
                    }
                } catch(FieldNotFoundException e)
                {
                    listener.onError(e.getMessage());
                }
            }
        }, new ResponseErrorHandler() {
            @Override
            public void onError(String error) {
                /**
                 * Handle the error!
                 */
                listener.onError(error);
            }
        });
    }

    public void retrieveCustomers(final ThingpleAPI.Listener<List<CustomerModel>> listener) throws APICallException {
        Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                /**
                 * Unserialize customers informations.
                 */
                try {
                    if(!response.has("data")) {
                        throw new FieldNotFoundException("Bad response from server, field ");
                    }

                    List<CustomerModel> customers_list = new LinkedList<>();
                    try {
                        JSONArray customers = response.getJSONArray("data");
                        for (int i = 0; i < customers.length(); i++) {
                            JSONObject customer = customers.getJSONObject(i);
                            customers_list.add(new CustomerModel(customer));
                        }
                        listener.onResponse(customers_list);
                    } catch (JSONException e)
                    {
                        listener.onError(e.getMessage());
                    }
                } catch(FieldNotFoundException e)
                {
                    listener.onError(e.getMessage());
                }
            }
        };

        try {
            Cache.Entry entry = this.network.getRequestQueue().getCache().get("/customers");
            if(entry == null)
            {
                throw new Exception("Empty cache!");
            }
            JSONObject cached = new JSONObject(new String(entry.data, "UTF-8"));
            responseListener.onResponse(cached);
            Log.i("ThingpleAPI", "Cache used!");
            return;
        } catch(JSONException e)
        {
            e.printStackTrace();
            Log.i("ThingpleAPI", "Cache not used! (" + e.getMessage() + ")");
        } catch(UnsupportedEncodingException e)
        {
            Log.i("ThingpleAPI", "Cache not used, invalid encoding!");
        } catch(Exception e)
        {
            Log.i("ThingpleAPI", "Exception: " + e.getMessage());
        }

        this.get("/customers", responseListener, new ResponseErrorHandler() {
            @Override
            public void onError(String error) {
                /**
                 * Handle the error!
                 */
                listener.onError(error);
            }
        });
    }

    public void retrieveProducts(final ThingpleAPI.Listener<List<ProductModel>> listener) throws APICallException {
        Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                /**
                 * Unserialize customers informations.
                 */
                try {
                    if(!response.has("data")) {
                        throw new FieldNotFoundException("Bad response from server, field ");
                    }

                    List<ProductModel> products_list = new LinkedList<>();
                    try {
                        JSONArray products = response.getJSONArray("data");
                        for (int i = 0; i < products.length(); i++) {
                            JSONObject product = products.getJSONObject(i);
                            products_list.add(ProductModel.getOrSet(product));
                        }
                        listener.onResponse(products_list);
                    } catch (JSONException e)
                    {
                        listener.onError(e.getMessage());
                    }
                } catch(FieldNotFoundException e)
                {
                    listener.onError(e.getMessage());
                }
            }
        };

        this.get("/products", responseListener, new ResponseErrorHandler() {
            @Override
            public void onError(String error) {
                /**
                 * Handle the error!
                 */
                listener.onError(error);
            }
        });
    }


    public void retrieveDeliveryPlans(final TruckTag truck, final ThingpleAPI.Listener<List<PlanItemModel>> listener) throws APICallException {
        Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                /**
                 * Unserialize delivery plans informations.
                 */
                try {
                    if(!response.has("data")) {
                        throw new FieldNotFoundException("Bad response from server, field ");
                    }

                    List<PlanItemModel> deliveryplans_list = new LinkedList<>();
                    try {
                        JSONArray deliveryplans = response.getJSONArray("data");
                        PlanItemModel.flush();
                        PlanGroupModel.flush();
                        for (int i = 0; i < deliveryplans.length(); i++) {
                            JSONObject deliveryplan = deliveryplans.getJSONObject(i);
                            deliveryplans_list.add(new PlanItemModel(deliveryplan));
                        }
                        listener.onResponse(deliveryplans_list);
                    } catch (JSONException e)
                    {
                        listener.onError(e.getMessage());
                    }
                } catch(FieldNotFoundException e)
                {
                    listener.onError(e.getMessage());
                }
            }
        };

        this.get("/" + truck.getTruckId() + "/plan/" + this.getDeviceId(), responseListener, new ResponseErrorHandler() {
            @Override
            public void onError(String error) {
                /**
                 * Handle the error!
                 */
                listener.onError(error);
            }
        });
    }

    public void updateCylinder(final EventModel event, final ThingpleAPI.Listener<JSONObject> listener) throws APICallException
    {
        JSONObject json = new JSONObject();
        try {
            json.accumulate("reader_reference", this.getDeviceId());
            json.accumulate("truck_id", event.truck_id);
            json.accumulate("truck_plate_number", event.truck_plate_number);
            json.accumulate("operation_id", event.operation_id);
            json.accumulate("timestamp", event.timestamp);
        } catch(JSONException e)
        {
            Log.e("ThingpleAPI", e.getMessage());
            e.printStackTrace();
            throw new APICallException("Unable to generate JSON: " + e.getMessage());
        }

        /**
         * Execute the request.
         */
        this.post("/" + event.huashen_number + "/cylinder", json, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                /**
                 * Update the cylinders informations.
                 */
                try {
                    JSONObject cylinder_json;
                    try {
                        cylinder_json = response.getJSONObject("data");
                    } catch(JSONException e)
                    {
                        throw new FieldNotFoundException("Missing field data");
                    }
                    listener.onResponse(cylinder_json);
                } catch(FieldNotFoundException e)
                {
                    listener.onError(e.getMessage());
                }
            }
        }, new ResponseErrorHandler() {
            @Override
            public void onError(String error) {
                /**
                 * Handle the error!
                 */
                listener.onError(error);
            }
        });
    }

    public void addAnomaly(final EventModel anomaly, final ThingpleAPI.Listener<JSONObject> listener) throws APICallException
    {
        JSONObject json = new JSONObject();
        try {
            json.accumulate("reader_reference", this.getDeviceId());
            json.accumulate("truck_id", anomaly.truck_id);
            json.accumulate("operation_id", anomaly.operation_id);
            json.accumulate("anomaly_phase", anomaly.anomaly_phase);
            json.accumulate("anomaly_inspection", anomaly.anomaly_inspection);
            json.accumulate("timestamp", anomaly.timestamp);
        } catch(JSONException e)
        {
            Log.e("ThingpleAPI", e.getMessage());
            e.printStackTrace();
            throw new APICallException("Unable to generate JSON: " + e.getMessage());
        }

        /**
         * Execute the request.
         */
        this.post("/" + anomaly.huashen_number + "/anomaly", json, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                /**
                 * Update the cylinders informations.
                 */
                try {
                    JSONObject cylinder_json;
                    try {
                        cylinder_json = response.getJSONObject("data");
                    } catch(JSONException e)
                    {
                        throw new FieldNotFoundException("Missing field data");
                    }
                    listener.onResponse(cylinder_json);
                } catch(FieldNotFoundException e)
                {
                    listener.onError(e.getMessage());
                }
            }
        }, new ResponseErrorHandler() {
            @Override
            public void onError(String error) {
                /**
                 * Handle the error!
                 */
                listener.onError(error);
            }
        });
    }
}
